import cards
import sqlite3
import simulate

class AbstractPlayer:
    money = 1000000
    current_bet = 0
    game = None
    name = None
    hole_cards = None
    hand_power = None
    folded = False
        
    def __init__(self):
        self.activity = {0: 0, 3: 0, 4: 0, 5: 0}
        self.calls = 0
        self.raises = 0
        self.context_log = {}
        self.temp_context_log = {}
        
        self.db = sqlite3.connect('hole_cards_100k_v2.db')
        self.cursor = self.db.cursor()
    
    def hand(self):
        return self.hole_cards + self.game.community_cards
    
    def get_hand_power(self):
        return cards.calc_cards_power(self.hand())
    def clear_bet(self):
        self.current_bet = 0
        
    def clear_temp_log(self):
        self.temp_context_log = {}
    
    def pay_blind(self, amount):
        self.money -= amount
        self.current_bet = amount
        self.game.add_to_pot(amount)
    
    def bet(self, amount):
        self.money -= amount
        self.current_bet += amount
        self.game.current_bet += amount
        self.game.add_to_pot(amount)
        self.log_action("bet")
        self.raises += 1
        print(str(self) + " bet $" + str(amount) + ".")
    
    def call(self):
        self.log_action("call")
        amount = self.game.current_bet - self.current_bet
        self.money -= amount
        self.current_bet = self.game.current_bet
        self.game.add_to_pot(amount)
        self.calls += 1
        print(str(self) + " called $" + str(amount) + ".")
        
    def check(self):
        self.log_action("check")
        self.calls += 1
        print(str(self) + " checked.")
        
    def fold(self):
        self.folded = True
        print(str(self) + " folded.")
    
    def unfold(self):
        self.folded = False
        
    def raise_amount(self, amount):
        amount = max(amount, self.game.min_raise())
        amount = min(amount, self.game.max_raise())
        
        self.log_action("raise")
        increase = amount - self.current_bet
        self.money -= increase
        self.current_bet = amount
        self.game.current_bet = amount
        self.game.add_to_pot(increase)
        self.raises += 1
        print(str(self) + " raised to $" + str(amount) + ".")
        
    def get_context(self, action):
        """ Returns a tuple representing the current context of the game """
        
        pot_odds = self.pot_odds()
        str_pot_odds = ""
        
        if pot_odds < 0.15:
            str_pot_odds = "small"
        elif pot_odds < 0.3:
            str_pot_odds = "medium"
        else:
            str_pot_odds = "large"
        # 1. num. community cards, 2. opponents, 3. pot odds, 4. action(call, bet, raise. check)
        return (len(self.game.community_cards), self.num_opponents(), str_pot_odds, action)
    
    def log_action(self, action):
        context = self.get_context(action)
        
        if context in self.temp_context_log:
            self.temp_context_log[context].append(self.get_probability())
        else:
            self.temp_context_log[context] = [self.get_probability()]
    
    def store_temp_log(self):
        for context, probability in self.temp_context_log.items():
            if context in self.context_log:
                self.context_log[context] += probability
            else:
                self.context_log[context] = probability
        
    def get_probability(self):
        if len(self.game.community_cards) == 0:
            return self.probability_pre_flop()
        
        return self.probability_post_flop()

    def get_simple_probability(self):
        if len(self.game.community_cards) == 0:
            return self.probability_pre_flop()
        
        return self.simple_probability_post_flop()

    
    def relative_activity(self):
        if self.game.hands_played < 5 or self.activity[len(self.game.community_cards)] == 0:
            # default activity
            return 1/(len(self.game.players) - 0.5)
            
        return self.activity[len(self.game.community_cards)]/self.game.hands_played
    
    def context_probability(self):
        if self.temp_context_log:
            current_context = self.get_context(sorted(self.temp_context_log, key = lambda e: e[3]).pop())
            if current_context in self.context_log:
                return (sum(self.context_log[current_context]) + 1 - self.relative_activity()) / (len(self.context_log[current_context]) + 1)
        
        return 1 - self.relative_activity()
    
    def opponent_probability_context(self):
        player_probability = self.game.player_propability_context(self)
        return sum(player_probability) / len(player_probability)
        
    def probability_pre_flop(self):
        # we want the smallest card as card1 to match our probability table
        if self.hole_cards[0] <= self.hole_cards[1]:
            card1, card2 = self.hole_cards
        else:
            card2, card1 = self.hole_cards
        
        parameters = (self.num_opponents(), card1[0], card2[0], cards.card_suit(card1) == cards.card_suit(card2))
        self.cursor.execute("SELECT probability FROM probabilities WHERE opponents = %d AND card1 = %d AND card2 = %d AND suited = %d" % parameters)
        
        return self.cursor.fetchone()[0]
    
    def simple_probability_post_flop(self):
        return simulate.simulate(self.hole_cards, self.num_opponents(), community_cards = self.game.community_cards)
    
    def probability_post_flop(self):
        return simulate.simulate_distributions(self.hole_cards, self.game.player_activity(self), community_cards = self.game.community_cards)

    def pot_odds(self):
        to_call = self.game.current_bet - self.current_bet
        return to_call/(self.game.pot + to_call)
        
    def num_opponents(self):
        return len(self.game.players_in_hand()) - 1